#include "sdk.h"
#include <algorithm>
#include <sstream>
#include <wx/toolbar.h>
#include "debuggermanager.h"
#include "M_Code_debugger_interfaces.h"
#include "loggers.h"
#include "manager.h"

cbWatch::cbWatch() :
  m_changed( true ),
  m_removed( false ),
  m_expanded( false ),
  m_autoUpdate( true ) {
}

cbWatch::~cbWatch() {
  m_children.clear();
}

void cbWatch::AddChild( cb::shared_ptr<cbWatch> parent, cb::shared_ptr<cbWatch> watch ) {
  watch->m_parent = parent;
  parent->m_children.push_back( watch );
}

void cbWatch::RemoveChild( int index ) {
  std::vector<cb::shared_ptr<cbWatch> >::iterator it = m_children.begin();
  std::advance( it, index );
  m_children.erase( it );
}

inline bool TestIfMarkedForRemoval( cb::shared_ptr<cbWatch> watch ) {
  if( watch->IsRemoved() ) {
    return true;
  } else {
    watch->RemoveMarkedChildren();
    return false;
  }
}

bool cbWatch::RemoveMarkedChildren() {
  size_t start_size = m_children.size();
  std::vector<cb::shared_ptr<cbWatch> >::iterator new_last;
  new_last = std::remove_if( m_children.begin(), m_children.end(), &TestIfMarkedForRemoval );
  m_children.erase( new_last, m_children.end() );
  return start_size != m_children.size();
}

void cbWatch::RemoveChildren() {
  m_children.clear();
}

int cbWatch::GetChildCount() const {
  return m_children.size();
}

cb::shared_ptr<cbWatch> cbWatch::GetChild( int index ) {
  std::vector<cb::shared_ptr<cbWatch> >::iterator it = m_children.begin();
  std::advance( it, index );
  return *it;
}

cb::shared_ptr<const cbWatch> cbWatch::GetChild( int index ) const {
  std::vector<cb::shared_ptr<cbWatch> >::const_iterator it = m_children.begin();
  std::advance( it, index );
  return *it;
}

cb::shared_ptr<cbWatch> cbWatch::FindChild( const wxString& symbol ) {
  for( std::vector<cb::shared_ptr<cbWatch> >::iterator it = m_children.begin(); it != m_children.end(); ++it ) {
    wxString s;
    ( *it )->GetSymbol( s );
    if( s == symbol ) {
      return *it;
    }
  }
  return cb::shared_ptr<cbWatch>();
}

int cbWatch::FindChildIndex( const wxString& symbol ) const {
  int index = 0;
  for( std::vector<cb::shared_ptr<cbWatch> >::const_iterator it = m_children.begin();
       it != m_children.end();
       ++it, ++index ) {
    wxString s;
    ( *it )->GetSymbol( s );
    if( s == symbol ) {
      return index;
    }
  }
  return -1;
}

cb::shared_ptr<const cbWatch> cbWatch::GetParent() const {
  return m_parent.lock();
}

cb::shared_ptr<cbWatch> cbWatch::GetParent() {
  return m_parent.lock();
}

bool cbWatch::IsRemoved() const {
  return m_removed;
}

bool cbWatch::IsChanged() const {
  return m_changed;
}

void cbWatch::MarkAsRemoved( bool flag ) {
  m_removed = flag;
}

void cbWatch::MarkChildsAsRemoved() {
  for( std::vector<cb::shared_ptr<cbWatch> >::iterator it = m_children.begin(); it != m_children.end(); ++it ) {
    ( *it )->MarkAsRemoved( true );
  }
}

void cbWatch::MarkAsChanged( bool flag ) {
  m_changed = flag;
}

void cbWatch::MarkAsChangedRecursive( bool flag ) {
  m_changed = flag;
  for( std::vector<cb::shared_ptr<cbWatch> >::iterator it = m_children.begin(); it != m_children.end(); ++it ) {
    ( *it )->MarkAsChangedRecursive( flag );
  }
}

bool cbWatch::IsExpanded() const {
  return m_expanded;
}

void cbWatch::Expand( bool expand ) {
  m_expanded = expand;
}

bool cbWatch::IsAutoUpdateEnabled() const {
  return m_autoUpdate;
}

void cbWatch::AutoUpdate( bool enabled ) {
  m_autoUpdate = enabled;
}

wxString cbWatch::MakeSymbolToAddress() const {
  wxString symbol;
  GetSymbol( symbol );
  return symbol;
}

bool cbWatch::IsPointerType() const {
  return false;
}

cb::shared_ptr<cbWatch> cbGetRootWatch( cb::shared_ptr<cbWatch> watch ) {
  cb::shared_ptr<cbWatch> root = watch;
  while( root ) {
    cb::shared_ptr<cbWatch> parent = root->GetParent();
    if( !parent ) {
      break;
    }
    root = parent;
  }
  return root;
}

cbStackFrame::cbStackFrame() :
  m_valid( false ) {
}

void cbStackFrame::SetNumber( int number ) {
  m_number = number;
}

void cbStackFrame::SetAddress( uint64_t address ) {
  m_address = address;
}

void cbStackFrame::SetSymbol( const wxString& symbol ) {
  m_symbol = symbol;
}

void cbStackFrame::SetFile( const wxString& filename, const wxString &line ) {
  m_file = filename;
  m_line = line;
}

void cbStackFrame::MakeValid( bool flag ) {
  m_valid = flag;
}

int cbStackFrame::GetNumber() const {
  return m_number;
}

uint64_t cbStackFrame::GetAddress() const {
  return m_address;
}

wxString cbStackFrame::GetAddressAsString() const {
  if( m_address != 0 ) {
    return cbDebuggerAddressToString( m_address );
  } else {
    return wxEmptyString;
  }
}

const wxString& cbStackFrame::GetSymbol() const {
  return m_symbol;
}

const wxString& cbStackFrame::GetFilename() const {
  return m_file;
}

const wxString& cbStackFrame::GetLine() const {
  return m_line;
}

bool cbStackFrame::IsValid() const {
  return m_valid;
}

cbThread::cbThread() {
}

cbThread::cbThread( bool active, int number, const wxString& info ) {
  m_active = active;
  m_number = number;
  m_info = info;
}

bool cbThread::IsActive() const {
  return m_active;
}

int cbThread::GetNumber() const {
  return m_number;
}

const wxString& cbThread::GetInfo() const {
  return m_info;
}

cbDebuggerConfiguration::cbDebuggerConfiguration( const ConfigManagerWrapper &config ) :
  m_config( config ),
  m_menuId( wxID_ANY ) {
}

cbDebuggerConfiguration::cbDebuggerConfiguration( const cbDebuggerConfiguration &o ) :
  m_config( o.m_config ),
  m_name( o.m_name ) {
}

void cbDebuggerConfiguration::SetName( const wxString &name ) {
  m_name = name;
}
const wxString& cbDebuggerConfiguration::GetName() const {
  return m_name;
}

const ConfigManagerWrapper& cbDebuggerConfiguration::GetConfig() const {
  return m_config;
}

void cbDebuggerConfiguration::SetConfig( const ConfigManagerWrapper &config ) {
  m_config = config;
}

void cbDebuggerConfiguration::SetMenuId( long id ) {
  m_menuId = id;
}

long cbDebuggerConfiguration::GetMenuId() const {
  return m_menuId;
}

bool M_Code_DebuggerCommonConfig::GetFlag( Flags flag ) {
  ConfigManager *c = Manager::Get()->GetConfigManager( _T( "debugger_common" ) );
  switch( flag ) {
    case AutoBuild:
      return c->ReadBool( _T( "/common/auto_build" ), true );
    case AutoSwitchFrame:
      return c->ReadBool( _T( "/common/auto_switch_frame" ), true );
    case ShowDebuggersLog:
      return c->ReadBool( _T( "/common/debug_log" ), false );
    case JumpOnDoubleClick:
      return c->ReadBool( _T( "/common/jump_on_double_click" ), false );
    case RequireCtrlForTooltips:
      return c->ReadBool( _T( "/common/require_ctrl_for_tooltips" ), false );
    case ShowTemporaryBreakpoints:
      return c->ReadBool( _T( "/common/show_temporary_breakpoints" ), false );
    default:
      return false;
  }
}

void M_Code_DebuggerCommonConfig::SetFlag( Flags flag, bool value ) {
  ConfigManager *c = Manager::Get()->GetConfigManager( _T( "debugger_common" ) );
  switch( flag ) {
    case AutoBuild:
      c->Write( _T( "/common/auto_build" ), value );
      break;
    case AutoSwitchFrame:
      c->Write( _T( "/common/auto_switch_frame" ), value );
      break;
    case ShowDebuggersLog:
      c->Write( _T( "/common/debug_log" ), value );
      break;
    case JumpOnDoubleClick:
      c->Write( _T( "/common/jump_on_double_click" ), value );
      break;
    case RequireCtrlForTooltips:
      c->Write( _T( "/common/require_ctrl_for_tooltips" ), value );
      break;
    case ShowTemporaryBreakpoints:
      c->Write( _T( "/common/show_temporary_breakpoints" ), value );
    default:
      ;
  }
}

wxString M_Code_DebuggerCommonConfig::GetValueTooltipFont() {
  wxFont system = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT );
  system.SetPointSize( std::max( system.GetPointSize() - 3, 7 ) );
  wxString defaultFont = system.GetNativeFontInfo()->ToString();
  ConfigManager *c = Manager::Get()->GetConfigManager( _T( "debugger_common" ) );
  wxString configFont = c->Read( _T( "/common/tooltip_font" ) );
  return configFont.empty() ? defaultFont : configFont;
}

void M_Code_DebuggerCommonConfig::SetValueTooltipFont( const wxString &font ) {
  const wxString &oldFont = GetValueTooltipFont();
  if( font != oldFont && !font.empty() ) {
    ConfigManager *c = Manager::Get()->GetConfigManager( _T( "debugger_common" ) );
    c->Write( _T( "/common/tooltip_font" ), font );
  }
}

M_Code_DebuggerCommonConfig::Perspective M_Code_DebuggerCommonConfig::GetPerspective() {
  ConfigManager *c = Manager::Get()->GetConfigManager( _T( "debugger_common" ) );
  int v = c->ReadInt( _T( "/common/perspective" ), static_cast<int>( OnePerDebuggerConfig ) );
  if( v < OnlyOne || v > OnePerDebuggerConfig ) {
    return OnePerDebuggerConfig;
  }
  return static_cast<Perspective>( v );
}

void M_Code_DebuggerCommonConfig::SetPerspective( int perspective ) {
  ConfigManager *c = Manager::Get()->GetConfigManager( _T( "debugger_common" ) );
  if( perspective < OnlyOne || perspective > OnePerDebuggerConfig ) {
    perspective = OnePerDebuggerConfig;
  }
  c->Write( _T( "/common/perspective" ), perspective );
}

wxString cbDetectDebuggerExecutable( const wxString &exeName ) {
  wxString exeExt( wxEmptyString );
  wxString exePath = cbFindFileInPATH( exeName );
  if( exePath.empty() ) {
    exePath = _T( "/usr/bin/" ) + exeName + exeExt;
  }
  if( !wxDirExists( exePath ) ) {
    return wxEmptyString;
  }
  return exePath + wxFileName::GetPathSeparator() + exeName + exeExt;
}

uint64_t cbDebuggerStringToAddress( const wxString &address ) {
  if( address.empty() ) {
    return 0;
  }
  std::istringstream s( address.utf8_str().data() );
  uint64_t result;
  s >> std::hex >> result;
  return ( s.fail() ? 0 : result );
}

wxString cbDebuggerAddressToString( uint64_t address ) {
  std::stringstream s;
  s << "0x" << std::hex << address;
  return wxString( s.str().c_str(), wxConvUTF8 );
}

class DebugTextCtrlLogger : public TextCtrlLogger {
  public:
    DebugTextCtrlLogger( bool fixedPitchFont, bool debugLog ) :
      TextCtrlLogger( fixedPitchFont ),
      m_panel( nullptr ),
      m_debugLog( debugLog ) {
    }
    wxWindow* CreateTextCtrl( wxWindow *parent ) {
      return TextCtrlLogger::CreateControl( parent );
    }
    virtual wxWindow* CreateControl( wxWindow* parent );
  private:
    wxPanel *m_panel;
    bool m_debugLog;
};

class DebugLogPanel : public wxPanel {
  public:
    DebugLogPanel( wxWindow *parent, DebugTextCtrlLogger *text_control_logger, bool debug_log ) :
      wxPanel( parent ),
      m_text_control_logger( text_control_logger ),
      m_debug_log( debug_log ) {
      int idDebug_LogEntryControl = wxNewId();
      int idDebug_ExecuteButton = wxNewId();
      int idDebug_ClearButton = wxNewId();
      int idDebug_LoadButton = wxNewId();
      wxBoxSizer *sizer = new wxBoxSizer( wxVERTICAL );
      wxBoxSizer *control_sizer = new wxBoxSizer( wxHORIZONTAL );
      wxWindow *text_control = text_control_logger->CreateTextCtrl( this );
      sizer->Add( text_control, wxEXPAND, wxEXPAND | wxALL, 0 );
      sizer->Add( control_sizer, 0, wxEXPAND | wxALL, 0 );
      wxStaticText *label = new wxStaticText( this, wxID_ANY, _T( "Command:" ),
                                              wxDefaultPosition, wxDefaultSize, wxST_NO_AUTORESIZE );
      m_command_entry = new wxComboBox( this, idDebug_LogEntryControl, wxEmptyString,
                                        wxDefaultPosition, wxDefaultSize, 0, nullptr,
                                        wxCB_DROPDOWN | wxTE_PROCESS_ENTER );
      wxBitmap execute_bitmap = wxArtProvider::GetBitmap( wxART_MAKE_ART_ID_FROM_STR( _T( "wxART_EXECUTABLE_FILE" ) ),
                                wxART_BUTTON );
      wxBitmap clear_bitmap = wxArtProvider::GetBitmap( wxART_MAKE_ART_ID_FROM_STR( _T( "wxART_DELETE" ) ), wxART_BUTTON );
      wxBitmap file_open_bitmap = wxArtProvider::GetBitmap( wxART_MAKE_ART_ID_FROM_STR( _T( "wxART_FILE_OPEN" ) ),
                                  wxART_BUTTON );
      wxBitmapButton *button_execute;
      button_execute = new wxBitmapButton( this, idDebug_ExecuteButton, execute_bitmap, wxDefaultPosition,
                                           wxDefaultSize, wxBU_AUTODRAW, wxDefaultValidator,
                                           _T( "idDebug_ExecuteButton" ) );
      button_execute->SetToolTip( _T( "执行当前命令" ) );
      wxBitmapButton *button_load = new wxBitmapButton( this, idDebug_LoadButton, file_open_bitmap, wxDefaultPosition,
          wxDefaultSize, wxBU_AUTODRAW, wxDefaultValidator, _T( "idDebug_LoadButton" ) );
      button_load->SetDefault();
      button_load->SetToolTip( _T( "从文件加载" ) );
      wxBitmapButton *button_clear = new wxBitmapButton( this, idDebug_ClearButton, clear_bitmap, wxDefaultPosition,
          wxDefaultSize, wxBU_AUTODRAW, wxDefaultValidator, _T( "idDebug_ClearButton" ) );
      button_clear->SetDefault();
      button_clear->SetToolTip( _T( "清除输出窗口" ) );
      control_sizer->Add( label, 0, wxALIGN_CENTER_VERTICAL | wxALL, 2 );
      control_sizer->Add( m_command_entry, wxEXPAND, wxEXPAND | wxALL, 2 );
      control_sizer->Add( button_execute, 0, wxEXPAND | wxALL, 0 );
      control_sizer->Add( button_load, 0, wxALL | wxALIGN_CENTER_VERTICAL, 0 );
      control_sizer->Add( button_clear, 0, wxALL | wxALIGN_CENTER_VERTICAL, 0 );
      SetSizer( sizer );
      Connect( idDebug_LogEntryControl, wxEVT_COMMAND_TEXT_ENTER,
               wxObjectEventFunction( &DebugLogPanel::OnEntryCommand ) );
      Connect( idDebug_ExecuteButton, wxEVT_COMMAND_BUTTON_CLICKED,
               wxObjectEventFunction( &DebugLogPanel::OnEntryCommand ) );
      Connect( idDebug_ClearButton, wxEVT_COMMAND_BUTTON_CLICKED,
               wxObjectEventFunction( &DebugLogPanel::OnClearLog ) );
      Connect( idDebug_LoadButton, wxEVT_COMMAND_BUTTON_CLICKED,
               wxObjectEventFunction( &DebugLogPanel::OnLoadFile ) );
      Connect( idDebug_ExecuteButton, wxEVT_UPDATE_UI,
               wxObjectEventFunction( &DebugLogPanel::OnUpdateUI ) );
      Connect( idDebug_LoadButton, wxEVT_UPDATE_UI,
               wxObjectEventFunction( &DebugLogPanel::OnUpdateUI ) );
      Connect( idDebug_LogEntryControl, wxEVT_UPDATE_UI,
               wxObjectEventFunction( &DebugLogPanel::OnUpdateUI ) );
    }

    void OnEntryCommand( M_Code_unused wxCommandEvent& event ) {
      assert( m_command_entry );
      wxString cmd = m_command_entry->GetValue();
      cmd.Trim( false );
      cmd.Trim( true );
      if( cmd.IsEmpty() ) {
        return;
      }
      cbDebuggerPlugin *plugin = Manager::Get()->GetDebuggerManager()->GetActiveDebugger();
      if( plugin ) {
        plugin->SendCommand( cmd, m_debug_log );
        int index = m_command_entry->FindString( cmd );
        if( index != wxNOT_FOUND ) {
          m_command_entry->Delete( index );
        }
        m_command_entry->Append( cmd );
        m_command_entry->SetValue( wxEmptyString );
      }
    }

    void OnClearLog( M_Code_unused wxCommandEvent& event ) {
      assert( m_command_entry );
      assert( m_text_control_logger );
      m_text_control_logger->Clear();
      m_command_entry->SetFocus();
    }

    void OnLoadFile( M_Code_unused wxCommandEvent& event ) {
      cbDebuggerPlugin *plugin = Manager::Get()->GetDebuggerManager()->GetActiveDebugger();
      if( !plugin ) {
        return;
      }
      ConfigManager* manager = Manager::Get()->GetConfigManager( _T( "app" ) );
      wxString path = manager->Read( _T( "/file_dialogs/file_run_dbg_script/directory" ), wxEmptyString );
      wxFileDialog dialog( this, _T( "加载脚本" ), path, wxEmptyString,
                           _T( "调试器脚本文件 (*.gdb)|*.gdb" ), wxFD_OPEN | compatibility::wxHideReadonly );
      if( dialog.ShowModal() == wxID_OK ) {
        manager->Write( _T( "/file_dialogs/file_run_dbg_script/directory" ), dialog.GetDirectory() );
        plugin->SendCommand( _T( "获取 " ) + dialog.GetPath(), m_debug_log );
      }
    }

    void OnUpdateUI( wxUpdateUIEvent &event ) {
      cbDebuggerPlugin *plugin = Manager::Get()->GetDebuggerManager()->GetActiveDebugger();
      event.Enable( plugin && plugin->IsRunning() && plugin->IsStopped() );
    }
  private:
    DebugTextCtrlLogger *m_text_control_logger;
    wxComboBox  *m_command_entry;
    bool m_debug_log;
};

wxWindow* DebugTextCtrlLogger::CreateControl( wxWindow* parent ) {
  if( !m_panel ) {
    m_panel = new DebugLogPanel( parent, this, m_debugLog );
  }
  return m_panel;
}

template<> DebuggerManager* Mgr<DebuggerManager>::instance = nullptr;
template<> bool  Mgr<DebuggerManager>::isShutdown = false;

inline void ReadActiveDebuggerConfig( wxString &name, int &configIndex ) {
  ConfigManager &config = *Manager::Get()->GetConfigManager( _T( "debugger_common" ) );
  name = config.Read( _T( "active_debugger" ), wxEmptyString );
  if( name.empty() ) {
    configIndex = -1;
  } else {
    configIndex = std::max( 0, config.ReadInt( _T( "active_debugger_config" ), 0 ) );
  }
}

inline void WriteActiveDebuggerConfig( const wxString &name, int configIndex ) {
  ConfigManager &configMgr = *Manager::Get()->GetConfigManager( _T( "debugger_common" ) );
  configMgr.Write( _T( "active_debugger" ), name );
  configMgr.Write( _T( "active_debugger_config" ), configIndex );
}

cbDebuggerConfiguration* DebuggerManager::PluginData::GetConfiguration( int index ) {
  if( m_configurations.empty() ) {
    cbAssert( false );
  }
  if( index >= static_cast<int>( m_configurations.size() ) ) {
    return nullptr;
  } else {
    return m_configurations[index];
  }
}

DebuggerManager::DebuggerManager() :
  m_interfaceFactory( nullptr ),
  m_activeDebugger( nullptr ),
  m_menuHandler( nullptr ),
  m_backtraceDialog( nullptr ),
  m_breakPointsDialog( nullptr ),
  m_cpuRegistersDialog( nullptr ),
  m_disassemblyDialog( nullptr ),
  m_examineMemoryDialog( nullptr ),
  m_threadsDialog( nullptr ),
  m_watchesDialog( nullptr ),
  m_logger( nullptr ),
  m_loggerIndex( -1 ),
  m_isDisassemblyMixedMode( false ),
  m_useTargetsDefault( false ) {
  typedef cbEventFunctor<DebuggerManager, CodeBlocksEvent> Event;
  Manager::Get()->RegisterEventSink( cbEVT_PROJECT_ACTIVATE, new Event( this, &DebuggerManager::OnProjectActivated ) );
  Manager::Get()->RegisterEventSink( cbEVT_PROJECT_OPEN, new Event( this, &DebuggerManager::OnProjectActivated ) );
  Manager::Get()->RegisterEventSink( cbEVT_BUILDTARGET_SELECTED, new Event( this, &DebuggerManager::OnTargetSelected ) );
  Manager::Get()->RegisterEventSink( cbEVT_SETTINGS_CHANGED, new Event( this, &DebuggerManager::OnSettingsChanged ) );
  Manager::Get()->RegisterEventSink( cbEVT_PLUGIN_LOADING_COMPLETE, new Event( this, &DebuggerManager::OnPluginLoadingComplete ) );
  wxString activeDebuggerName;
  int activeConfig;
  ReadActiveDebuggerConfig( activeDebuggerName, activeConfig );
  if( activeDebuggerName.empty() && activeConfig == -1 ) {
    m_useTargetsDefault = true;
  }
  ConfigManager *c = Manager::Get()->GetConfigManager( _T( "debugger_common" ) );
  m_isDisassemblyMixedMode = c->ReadBool( _T( "/common/disassembly/mixed_mode" ), false );
}

DebuggerManager::~DebuggerManager() {
  for( RegisteredPlugins::iterator it = m_registered.begin(); it != m_registered.end(); ++it ) {
    it->second.ClearConfigurations();
  }
  Manager::Get()->RemoveAllEventSinksFor( this );
  delete m_interfaceFactory;
}

bool DebuggerManager::RegisterDebugger( cbDebuggerPlugin *plugin ) {
  RegisteredPlugins::iterator it = m_registered.find( plugin );
  if( it != m_registered.end() ) {
    return false;
  }
  const wxString &guiName = plugin->GetGUIName();
  const wxString &settingsName = plugin->GetSettingsName();
  wxRegEx regExSettingsName( _T( "^[a-z_][a-z0-9_]+$" ) );
  if( !regExSettingsName.Matches( settingsName ) ) {
    wxString s;
    s = F( _T( "调试器插件的设置名称 \"%s\" - \"%s\" 包含无效字符" ), guiName.c_str(), settingsName.c_str() );
    Manager::Get()->GetLogManager()->LogError( s );
    return false;
  }
  int normalIndex = -1;
  GetLogger( normalIndex );
  plugin->SetupLog( normalIndex );
  PluginData data;
  m_registered[plugin] = data;
  it = m_registered.find( plugin );
  ProcessSettings( it );
  cbAssert( !it->second.GetConfigurations().empty() );
  wxString activeDebuggerName;
  int activeConfig;
  ReadActiveDebuggerConfig( activeDebuggerName, activeConfig );
  if( activeDebuggerName == settingsName ) {
    if( activeConfig > static_cast<int>( it->second.GetConfigurations().size() ) ) {
      activeConfig = 0;
    }
    m_activeDebugger = plugin;
    m_activeDebugger->SetActiveConfig( activeConfig );
    m_menuHandler->SetActiveDebugger( m_activeDebugger );
  }
  CreateWindows();
  m_menuHandler->RebuildMenus();
  return true;
}

bool DebuggerManager::UnregisterDebugger( cbDebuggerPlugin *plugin ) {
  RegisteredPlugins::iterator it = m_registered.find( plugin );
  if( it == m_registered.end() ) {
    return false;
  }
  it->second.ClearConfigurations();
  m_registered.erase( it );
  if( plugin == m_activeDebugger ) {
    if( m_registered.empty() ) {
      m_activeDebugger = nullptr;
    } else
    { m_activeDebugger = m_registered.begin()->first; }
    m_menuHandler->SetActiveDebugger( m_activeDebugger );
  }
  if( !Manager::IsAppShuttingDown() ) {
    m_menuHandler->RebuildMenus();
    RefreshUI();
  }
  if( m_registered.empty() ) {
    DestoryWindows();
    if( Manager::Get()->GetLogManager() ) {
      Manager::Get()->GetDebuggerManager()->HideLogger();
    }
  }
  return true;
}

void DebuggerManager::ProcessSettings( RegisteredPlugins::iterator it ) {
  cbDebuggerPlugin *plugin = it->first;
  PluginData &data = it->second;
  ConfigManager *config = Manager::Get()->GetConfigManager( _T( "debugger_common" ) );
  wxString path = _T( "/sets/" ) + plugin->GetSettingsName();
  wxArrayString configs = config->EnumerateSubPaths( path );
  configs.Sort();
  if( configs.empty() ) {
    config->Write( path + _T( "/conf1/name" ), wxString( _T( "Default" ) ) );
    configs = config->EnumerateSubPaths( path );
    configs.Sort();
  }
  data.ClearConfigurations();
  data.m_lastConfigID = -1;
  for( size_t jj = 0; jj < configs.Count(); ++jj ) {
    wxString configPath = path + _T( '/' ) + configs[jj];
    wxString name = config->Read( configPath + _T( "/name" ) );
    cbDebuggerConfiguration *pluginConfig;
    pluginConfig = plugin->LoadConfig( ConfigManagerWrapper( _T( "debugger_common" ), configPath + _T( "/values" ) ) );
    if( pluginConfig ) {
      pluginConfig->SetName( name );
      data.GetConfigurations().push_back( pluginConfig );
    }
  }
}

ConfigManagerWrapper DebuggerManager::NewConfig( cbDebuggerPlugin *plugin, M_Code_unused const wxString& name ) {
  RegisteredPlugins::iterator it = m_registered.find( plugin );
  if( it == m_registered.end() ) {
    return ConfigManagerWrapper();
  }
  wxString path = _T( "/sets/" ) + it->first->GetSettingsName();
  if( it->second.m_lastConfigID == -1 ) {
    ConfigManager *config = Manager::Get()->GetConfigManager( _T( "debugger_common" ) );
    wxArrayString configs = config->EnumerateSubPaths( path );
    for( size_t ii = 0; ii < configs.GetCount(); ++ii ) {
      long id;
      if( configs[ii].Remove( 0, 4 ).ToLong( &id ) ) {
        it->second.m_lastConfigID = std::max<long>( it->second.m_lastConfigID, id );
      }
    }
  }
  path << _T( "/conf" ) << ++it->second.m_lastConfigID;
  return ConfigManagerWrapper( _T( "debugger_common" ), path +  _T( "/values" ) );
}

void DebuggerManager::RebuildAllConfigs() {
  for( RegisteredPlugins::iterator it = m_registered.begin(); it != m_registered.end(); ++it ) {
    ProcessSettings( it );
  }
  m_menuHandler->RebuildMenus();
}

bool DebuggerManager::HasMenu() const {
  wxMenuBar *menuBar = Manager::Get()->GetAppFrame()->GetMenuBar();
  cbAssert( menuBar );
  int menu_pos = menuBar->FindMenu( _T( "调试(&D)" ) );
  return menu_pos != wxNOT_FOUND;
}

void DebuggerManager::BuildContextMenu( wxMenu &menu, const wxString& word_at_caret, bool is_running ) {
  m_menuHandler->BuildContextMenu( menu, word_at_caret, is_running );
}

TextCtrlLogger* DebuggerManager::GetLogger( int &index ) {
  LogManager* msgMan = Manager::Get()->GetLogManager();
  if( !m_logger ) {
    m_logger = new DebugTextCtrlLogger( true, false );
    m_loggerIndex = msgMan->SetLog( m_logger );
    LogSlot &slot = msgMan->Slot( m_loggerIndex );
    slot.title = _T( "调试器" );
    wxString prefix = ConfigManager::GetDataFolder() + _T( "/images/misc.png" );
    wxBitmap* bmp = new wxBitmap( M_Code_LoadBitmap( prefix, wxBITMAP_TYPE_PNG ) );
    slot.icon = bmp;
    CodeBlocksLogEvent evtAdd( cbEVT_ADD_LOG_WINDOW, m_logger, slot.title, slot.icon );
    Manager::Get()->ProcessEvent( evtAdd );
  }
  index = m_loggerIndex;
  return m_logger;
}

TextCtrlLogger* DebuggerManager::GetLogger() {
  int index;
  return GetLogger( index );
}

void DebuggerManager::HideLogger() {
  CodeBlocksLogEvent evt( cbEVT_REMOVE_LOG_WINDOW, m_logger );
  Manager::Get()->ProcessEvent( evt );
  m_logger = nullptr;
  m_loggerIndex = -1;
}

void DebuggerManager::SetInterfaceFactory( cbDebugInterfaceFactory *factory ) {
  cbAssert( !m_interfaceFactory );
  m_interfaceFactory = factory;
  CreateWindows();
  m_backtraceDialog->EnableWindow( false );
  m_cpuRegistersDialog->EnableWindow( false );
  m_disassemblyDialog->EnableWindow( false );
  m_examineMemoryDialog->EnableWindow( false );
  m_threadsDialog->EnableWindow( false );
}

void DebuggerManager::CreateWindows() {
  if( !m_backtraceDialog ) {
    m_backtraceDialog = m_interfaceFactory->CreateBacktrace();
  }
  if( !m_breakPointsDialog ) {
    m_breakPointsDialog = m_interfaceFactory->CreateBreapoints();
  }
  if( !m_cpuRegistersDialog ) {
    m_cpuRegistersDialog = m_interfaceFactory->CreateCPURegisters();
  }
  if( !m_disassemblyDialog ) {
    m_disassemblyDialog = m_interfaceFactory->CreateDisassembly();
  }
  if( !m_examineMemoryDialog ) {
    m_examineMemoryDialog = m_interfaceFactory->CreateMemory();
  }
  if( !m_threadsDialog ) {
    m_threadsDialog = m_interfaceFactory->CreateThreads();
  }
  if( !m_watchesDialog ) {
    m_watchesDialog = m_interfaceFactory->CreateWatches();
  }
}

void DebuggerManager::DestoryWindows() {
  m_interfaceFactory->DeleteBacktrace( m_backtraceDialog );
  m_backtraceDialog = nullptr;
  m_interfaceFactory->DeleteBreakpoints( m_breakPointsDialog );
  m_breakPointsDialog = nullptr;
  m_interfaceFactory->DeleteCPURegisters( m_cpuRegistersDialog );
  m_cpuRegistersDialog = nullptr;
  m_interfaceFactory->DeleteDisassembly( m_disassemblyDialog );
  m_disassemblyDialog = nullptr;
  m_interfaceFactory->DeleteMemory( m_examineMemoryDialog );
  m_examineMemoryDialog = nullptr;
  m_interfaceFactory->DeleteThreads( m_threadsDialog );
  m_threadsDialog = nullptr;
  m_interfaceFactory->DeleteWatches( m_watchesDialog );
  m_watchesDialog = nullptr;
}

cbDebugInterfaceFactory* DebuggerManager::GetInterfaceFactory() {
  return m_interfaceFactory;
}

void DebuggerManager::SetMenuHandler( cbDebuggerMenuHandler *handler ) {
  m_menuHandler = handler;
}

cbDebuggerMenuHandler* DebuggerManager::GetMenuHandler() {
  return m_menuHandler;
}

cbBacktraceDlg* DebuggerManager::GetBacktraceDialog() {
  return m_backtraceDialog;
}

cbBreakpointsDlg* DebuggerManager::GetBreakpointDialog() {
  return m_breakPointsDialog;
}

cbCPURegistersDlg* DebuggerManager::GetCPURegistersDialog() {
  return m_cpuRegistersDialog;
}

cbDisassemblyDlg* DebuggerManager::GetDisassemblyDialog() {
  return m_disassemblyDialog;
}

cbExamineMemoryDlg* DebuggerManager::GetExamineMemoryDialog() {
  return m_examineMemoryDialog;
}

cbThreadsDlg* DebuggerManager::GetThreadsDialog() {
  return m_threadsDialog;
}

cbWatchesDlg* DebuggerManager::GetWatchesDialog() {
  return m_watchesDialog;
}

bool DebuggerManager::ShowBacktraceDialog() {
  cbBacktraceDlg *dialog = GetBacktraceDialog();
  if( !IsWindowReallyShown( dialog->GetWindow() ) ) {
    CodeBlocksDockEvent evt( cbEVT_SHOW_DOCK_WINDOW );
    evt.pWindow = dialog->GetWindow();
    Manager::Get()->ProcessEvent( evt );
    return true;
  } else {
    return false;
  }
}

bool DebuggerManager::UpdateBacktrace() {
  return m_backtraceDialog && IsWindowReallyShown( m_backtraceDialog->GetWindow() );
}

bool DebuggerManager::UpdateCPURegisters() {
  return m_cpuRegistersDialog && IsWindowReallyShown( m_cpuRegistersDialog->GetWindow() );
}

bool DebuggerManager::UpdateDisassembly() {
  return m_disassemblyDialog && IsWindowReallyShown( m_disassemblyDialog->GetWindow() );
}

bool DebuggerManager::UpdateExamineMemory() {
  return m_examineMemoryDialog && IsWindowReallyShown( m_examineMemoryDialog->GetWindow() );
}

bool DebuggerManager::UpdateThreads() {
  return m_threadsDialog && IsWindowReallyShown( m_threadsDialog->GetWindow() );
}

cbDebuggerPlugin* DebuggerManager::GetDebuggerHavingWatch( cb::shared_ptr<cbWatch> watch ) {
  watch = cbGetRootWatch( watch );
  for( RegisteredPlugins::iterator it = m_registered.begin(); it != m_registered.end(); ++it ) {
    if( it->first->HasWatch( watch ) ) {
      return it->first;
    }
  }
  return NULL;
}

bool DebuggerManager::ShowValueTooltip( const cb::shared_ptr<cbWatch> &watch, const wxRect &rect ) {
  return m_interfaceFactory->ShowValueTooltip( watch, rect );
}

DebuggerManager::RegisteredPlugins const & DebuggerManager::GetAllDebuggers() const {
  return m_registered;
}
DebuggerManager::RegisteredPlugins & DebuggerManager::GetAllDebuggers() {
  return m_registered;
}
cbDebuggerPlugin* DebuggerManager::GetActiveDebugger() {
  return m_activeDebugger;
}

inline void RefreshBreakpoints( M_Code_unused const cbDebuggerPlugin* plugin ) {
  EditorManager *editorManager = Manager::Get()->GetEditorManager();
  int count = editorManager->GetEditorsCount();
  for( int ii = 0; ii < count; ++ii ) {
    EditorBase *editor = editorManager->GetEditor( ii );
    if( editor->IsBuiltinEditor() ) {
      static_cast<M_Code_Editor*>( editor )->RefreshBreakpointMarkers();
    }
  }
}

void DebuggerManager::SetActiveDebugger( cbDebuggerPlugin* activeDebugger, ConfigurationVector::const_iterator config ) {
  RegisteredPlugins::const_iterator it = m_registered.find( activeDebugger );
  cbAssert( it != m_registered.end() );
  m_useTargetsDefault = false;
  m_activeDebugger = activeDebugger;
  int index = std::distance( it->second.GetConfigurations().begin(), config );
  m_activeDebugger->SetActiveConfig( index );
  WriteActiveDebuggerConfig( it->first->GetSettingsName(), index );
  RefreshUI();
}

void DebuggerManager::RefreshUI() {
  m_menuHandler->SetActiveDebugger( m_activeDebugger );
  m_menuHandler->RebuildMenus();
  RefreshBreakpoints( m_activeDebugger );
  if( m_activeDebugger ) {
    if( m_backtraceDialog ) {
      m_backtraceDialog->EnableWindow( m_activeDebugger->SupportsFeature( cbDebuggerFeature::Callstack ) );
    }
    if( m_cpuRegistersDialog ) {
      m_cpuRegistersDialog->EnableWindow( m_activeDebugger->SupportsFeature( cbDebuggerFeature::CPURegisters ) );
    }
    if( m_disassemblyDialog ) {
      m_disassemblyDialog->EnableWindow( m_activeDebugger->SupportsFeature( cbDebuggerFeature::Disassembly ) );
    }
    if( m_examineMemoryDialog ) {
      m_examineMemoryDialog->EnableWindow( m_activeDebugger->SupportsFeature( cbDebuggerFeature::ExamineMemory ) );
    }
    if( m_threadsDialog ) {
      m_threadsDialog->EnableWindow( m_activeDebugger->SupportsFeature( cbDebuggerFeature::Threads ) );
    }
  }
  if( m_watchesDialog ) {
    m_watchesDialog->RefreshUI();
  }
  if( m_breakPointsDialog ) {
    m_breakPointsDialog->Reload();
  }
}

bool DebuggerManager::IsActiveDebuggerTargetsDefault() const {
  return m_activeDebugger && m_useTargetsDefault;
}

void DebuggerManager::SetTargetsDefaultAsActiveDebugger() {
  m_activeDebugger = nullptr;
  m_menuHandler->SetActiveDebugger( nullptr );
  FindTargetsDebugger();
}

void DebuggerManager::FindTargetsDebugger() {
  if( Manager::Get()->GetProjectManager()->IsLoadingOrClosing() ) {
    return;
  }
  m_activeDebugger = nullptr;
  m_menuHandler->SetActiveDebugger( nullptr );
  if( m_registered.empty() ) {
    m_menuHandler->MarkActiveTargetAsValid( false );
    return;
  }
  ProjectManager* projectMgr = Manager::Get()->GetProjectManager();
  LogManager* log = Manager::Get()->GetLogManager();
  M_Code_Project* project = projectMgr->GetActiveProject();
  ProjectBuildTarget *target = nullptr;
  if( project ) {
    const wxString &targetName = project->GetActiveBuildTarget();
    if( project->BuildTargetValid( targetName ) ) {
      target = project->GetBuildTarget( targetName );
    }
  }
  Compiler *compiler = nullptr;
  if( !target ) {
    if( project ) {
      compiler = CompilerFactory::GetCompiler( project->GetCompilerID() );
    }
    if( !compiler ) {
      compiler = CompilerFactory::GetDefaultCompiler();
    }
    if( !compiler ) {
      log->LogError( _T( "无法获取活动 目标/项目/默认目标 的编译器!" ) );
      m_menuHandler->MarkActiveTargetAsValid( false );
      return;
    }
  } else {
    compiler = CompilerFactory::GetCompiler( target->GetCompilerID() );
    if( !compiler ) {
      log->LogError( F( _T( "当前目标 '%s' 没有有效的编译器!" ), target->GetTitle().c_str() ) );
      m_menuHandler->MarkActiveTargetAsValid( false );
      return;
    }
  }
  wxString dbgString = compiler->GetPrograms().DBGconfig;
  wxString::size_type pos = dbgString.find( _T( ':' ) );
  wxString name, config;
  if( pos != wxString::npos ) {
    name = dbgString.substr( 0, pos );
    config = dbgString.substr( pos + 1, dbgString.length() - pos - 1 );
  }
  if( name.empty() || config.empty() ) {
    if( compiler->GetID() != _T( "null" ) ) {
      log->LogError( F( _T( "当前编译器 '%s' 没有有效的调试器!" ), compiler->GetName().c_str() ) );
    }
    m_menuHandler->MarkActiveTargetAsValid( false );
    return;
  }
  for( RegisteredPlugins::iterator it = m_registered.begin(); it != m_registered.end(); ++it ) {
    PluginData &data = it->second;
    if( it->first->GetSettingsName() == name ) {
      ConfigurationVector &configs = data.GetConfigurations();
      int index = 0;
      for( ConfigurationVector::iterator itConf = configs.begin(); itConf != configs.end(); ++itConf, ++index ) {
        if( ( *itConf )->GetName() == config ) {
          m_activeDebugger = it->first;
          m_activeDebugger->SetActiveConfig( index );
          m_useTargetsDefault = true;
          WriteActiveDebuggerConfig( wxEmptyString, -1 );
          RefreshUI();
          m_menuHandler->MarkActiveTargetAsValid( true );
          return;
        }
      }
    }
  }
  wxString targetTitle( target ? target->GetTitle() : _T( "<nullptr>" ) );
  log->LogError( F( _T( "找不到调试器配置: '%s:%s' 在当前目标 '%s'!" ), name.c_str(), config.c_str(), targetTitle.c_str() ) );
  m_menuHandler->MarkActiveTargetAsValid( false );
}

bool DebuggerManager::IsDisassemblyMixedMode() {
  return m_isDisassemblyMixedMode;
}

void DebuggerManager::SetDisassemblyMixedMode( bool mixed ) {
  m_isDisassemblyMixedMode = mixed;
  ConfigManager *c = Manager::Get()->GetConfigManager( _T( "debugger_common" ) );
  c->Write( _T( "/common/disassembly/mixed_mode" ), m_isDisassemblyMixedMode );
}

void DebuggerManager::OnProjectActivated( M_Code_unused CodeBlocksEvent& event ) {
  if( m_useTargetsDefault ) {
    FindTargetsDebugger();
  }
}

void DebuggerManager::OnTargetSelected( M_Code_unused CodeBlocksEvent& event ) {
  if( m_useTargetsDefault ) {
    FindTargetsDebugger();
  }
}

void DebuggerManager::OnSettingsChanged( CodeBlocksEvent& event ) {
  if( event.GetInt() == cbSettingsType::Compiler || event.GetInt() == cbSettingsType::Debugger ) {
    if( m_useTargetsDefault ) {
      FindTargetsDebugger();
    }
  }
}

void DebuggerManager::OnPluginLoadingComplete( M_Code_unused CodeBlocksEvent& event ) {
  RefreshUI();
  if( !m_activeDebugger ) {
    m_useTargetsDefault = true;
    FindTargetsDebugger();
  }
}
